﻿using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Spacer {
	public enum AnimationMode {
		None,
		Forward,
		Reverse
	}

	public enum DrawMode {
		None,
		Texture,
		Sprite
	}

	//----------------------------------------------------------------------------------------------------

	public class Drawable {
		public Drawable() {}

		public Drawable( SolarSys world ) {
			World = world;
			if( this.GetType() == typeof(Drawable) ) Initialize();
		}

		public virtual void Initialize() {
			if( World == null )
				throw new NullReferenceException( "World must be assigned before initialization!" );

			if( !(this is Entity) )
				World.AddedDrawables.Add( this );

			DrawMode = DrawMode.Texture;
			Texture = Main.Textures["Missing"];
			DrawColor = Color.White;
			DrawScale = new Vector2( 1f, 1f );
			Position = Vector2.Zero;
			Velocity = Vector2.Zero;
			Acceleration = Vector2.Zero;

			IsMarkedForRemoval = false;
			CreationTime = Globals.GameTime;
			LifeTime = 0f;

			m_strSequence = "Idle";
		}

		// ----------

		public virtual void Update() {
			if( (LifeTime != 0f && Globals.GameTime >= CreationTime + LifeTime) || (Parent != null && Parent.IsMarkedForRemoval) )
				IsMarkedForRemoval = true;

			if( Parent != null ) {
				Position = Parent.Position + Vector2.Transform( LocalPosition, Matrix.CreateRotationZ(Parent.Angle) );
				Angle = Parent.Angle + LocalAngle; // TODO: Is this right?
			} else {
				Velocity += Acceleration * Globals.UpdateDeltaTime;
				Velocity *= (1f - (LinearDamping * Globals.UpdateDeltaTime));
				Position += Velocity * Globals.UpdateDeltaTime;
				
				AngularVelocity += AngularAcceleration * Globals.UpdateDeltaTime;
				AngularVelocity *= (1f - (AngularDamping * Globals.UpdateDeltaTime));
				Angle += AngularVelocity * Globals.UpdateDeltaTime;
			}
		}

		public virtual void Draw() {
			if( IsMarkedForRemoval || DrawMode == DrawMode.None || (DrawMode == DrawMode.Sprite && Sprite == null) || (DrawMode == DrawMode.Texture && Texture == null) )
				return;

			if( DrawMode == DrawMode.Sprite ) {
				SpriteSequence sequence = Sprite.GetSequence( m_strSequence );
				if( sequence != null ) {
					if( AnimationMode != AnimationMode.None ) {
						if( m_fLastFrameChange + (1f / (sequence.FrameRate * AnimationScale)) <= Globals.GameTime ) {
							if( m_fLastFrameChange != 0f ) {

								if( AnimationMode == AnimationMode.Forward ) {
									FrameCurrent++;
									if( FrameCurrent > sequence.EndFrame ) {
										if( sequence.Loop )
											FrameCurrent = sequence.StartFrame;
										else
											FrameCurrent = sequence.EndFrame;
									}
								}  else if( AnimationMode == AnimationMode.Reverse ) {
									FrameCurrent--;
									if( FrameCurrent < sequence.StartFrame ) {
										if( sequence.Loop )
											FrameCurrent = sequence.EndFrame;
										else
											FrameCurrent = sequence.StartFrame;
									}
								}

							}
							m_fLastFrameChange = Globals.GameTime;
						}
					}

					Surface.DrawColor = DrawColor;
					Surface.DrawSubRect(
						Sprite.Texture,
						Position,
						new Vector2( Sprite.Size.X*DrawScale.X, Sprite.Size.Y*DrawScale.Y ),
						Sprite.GetFrameRect(FrameCurrent),
						Sprite.Center + DrawOrigin,
						Angle,
						DrawLayer
					);
				}
			} else if( DrawMode == DrawMode.Texture ) {
				Surface.DrawColor = DrawColor;
				Surface.DrawRect(
					Texture,
					Position,
					new Vector2( Texture.Width*DrawScale.X, Texture.Height*DrawScale.Y ),
					DrawOrigin,
					Angle,
					DrawLayer
				);
			}
		}

		public virtual void Remove() {
			if( IsMarkedForRemoval )
				return;

			IsMarkedForRemoval = true;
			World.RemovedDrawables.Add( this );
		}
		
		public virtual void OnRemove() {}

		// ----------

		public SolarSys World { get; set; }

		// Base Drawable properties
		public DrawMode DrawMode { get; set; }

		public Texture2D Texture { get; set; }
		public string TextureName {
			get { return (Texture != null ? Texture.Name : ""); }
			set { Texture = Main.FindTexture( value ); }
		}

		public Sprite Sprite { get; set; }
		public string SpriteName {
			get { return (Sprite != null ? Sprite.Name : ""); }
			set { Sprite = Main.FindSprite( value ); }
		}

		public Color DrawColor { get; set; }
		public Vector2 DrawScale { get; set; }
		public float DrawLayer { get; set; }
		public Vector2 DrawOrigin { get; set; }

		public virtual Vector2 Position { get; set; }
		public virtual Vector2 Velocity { get; set; }
		public Vector2 Acceleration { get; set; }

		public virtual float Angle { get; set; }
		public virtual float AngularVelocity { get; set; }
		public float AngularAcceleration { get; set; }
		
		public virtual float LinearDamping { get; set; }
		public virtual float AngularDamping { get; set; }
		public Drawable Parent { get; set; }


		public Vector2 LocalPosition { get; set; }
		public float LocalAngle { get; set; }

		// Required fields (Animation)
		private string m_strSequence;
		private float m_fLastFrameChange;

		// Animation properties (Sprite only)
		public string Sequence {
			get { return m_strSequence; }
			set {
				if( Sprite == null )
					return;

				m_strSequence = value;

				SpriteSequence sequence = Sprite.GetSequence( m_strSequence );
				if( sequence != null ) {
					FrameCurrent = sequence.StartFrame;
					m_fLastFrameChange = 0;
				}
			}
		}
		public AnimationMode AnimationMode { get; set; }
		public int FrameCurrent { get; set; }
		public float AnimationScale { get; set; }

		// Creation and death
		public bool IsMarkedForRemoval { get; protected set; }

		public float CreationTime { get; set; }
		public float LifeTime { get; set; }
		public float DieTime {
			get { return CreationTime + LifeTime; }
		}
	}
}
